home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C++ / Applications / NeuroSim 1.0 / .cp / CNeuralNet.cp < prev    next >
Text File  |  1996-02-19  |  13KB  |  396 lines

  1. // ===========================================================================
  2. //    CNeuralNet.cp                ©1996 Timo Eloranta
  3. // ===========================================================================
  4. //  An abstract neural net class - derived from LPeriodical to 
  5. //    receive a function call (SpendTime()) at regular intervals
  6.  
  7. #include "CNeuralNet.h"
  8. #include "NS_Utils.h"
  9. #include "CNeuroSimPane.h"
  10.  
  11. // ---------------------------------------------------------------------------
  12. //        • CNeuralNet
  13. //
  14. //          Called by:    CStdNeuralNet::CStdNeuralNet
  15. // ---------------------------------------------------------------------------
  16. //    Constructor. Can't be called with 'new', since CNeuralNet is
  17. //    an abstract class.
  18.  
  19. CNeuralNet::CNeuralNet( Uint16 inSize )
  20. {
  21.     mSize        = inSize;
  22.     mMatrix        = NULL;
  23.     mPane        = NULL;
  24.     
  25.     mDemoMode    = false;
  26.     
  27.     // Put this object to the static Idler queue of LPeriodical.
  28.     // As a result our SpendTime-function starts getting called
  29.     // after every Null Event.
  30.     
  31.     StartIdling();    
  32. }
  33.  
  34. // ---------------------------------------------------------------------------
  35. //        • GetNeuron
  36. //
  37. //          Called by:    CNeuralNet::InitMatrix, GenerateConnections, etc.
  38. //                        CNeuroSimPane::ClickSelf
  39. // ---------------------------------------------------------------------------
  40. //    The matrix of neurons is implemented as an array. This function
  41. //    returns a pointer to the neuron in the given column & row.
  42. //    Note that there is no bounds checking here because we want this to
  43. //    be as fast as possible. Column and row should both be > 0 and <= mSize !!
  44.  
  45. inline CNeuronPtr
  46. CNeuralNet::GetNeuron( Uint16 inCol, Uint16 inRow ) const
  47. {
  48.     return & mMatrix[ inCol - 1 + ((inRow - 1) * mSize) ];
  49. }
  50.  
  51. // ---------------------------------------------------------------------------
  52. //        • RequestDraw
  53. //
  54. //          Called by:    CNeuralNet::ProcessLightQ
  55. //                        CStdNeuron::DoClickAction
  56. // ---------------------------------------------------------------------------
  57. //    Pass the draw request to the pane.
  58.  
  59. inline void 
  60. CNeuralNet::RequestDraw() const
  61. {
  62.     mPane -> InvalidateDrawing();
  63. }
  64.  
  65. // ---------------------------------------------------------------------------
  66. //        • AddToLightQ
  67. //
  68. //          Called by:    CNeuron::IncState
  69. //                        CStdNeuron::DoClickAction
  70. // ---------------------------------------------------------------------------
  71. //    Add the given neuron pointer (LLink pointer actually) to the end
  72. //    of the "light up queue".
  73.  
  74. inline void
  75. CNeuralNet::AddToLightQ( LLink * inNeuron )
  76. {
  77.     mLightQueue.NextPut( inNeuron );
  78. }
  79.  
  80. // ---------------------------------------------------------------------------
  81. //        • ProcessLightQ
  82. //
  83. //          Called by:    CNeuralNet::SpendTime
  84. //                        CStdNeuron::DoClickAction
  85. // ---------------------------------------------------------------------------
  86. //    Here we process every neuron which has found its way to the 
  87. //    "light up queue". The main idea is that the processing is done in
  88. //    little groups - "waves". A single wave includes all such neurons
  89. //    which should light up simultaneously because their lighting up is
  90. //    caused by the same neuron or an equally long chain of neurons lighting
  91. //    up... The illusion of simultaneus lighting up is achieved by requesting
  92. //    the pane to be redrawn only after all the neurons in the same wave have
  93. //    been lit up (with the DoLightUpAction function). For a simultaneus
  94. //    fading we use a local "fade queue" to which every neuron is added 
  95. //    after it has been lit up. Waves are processed as long as there are  
  96. //    new neurons in the "light up queue".
  97.  
  98. void
  99. CNeuralNet::ProcessLightQ()
  100. {
  101.     CNeuronPtr    theNeuron;
  102.     Uint16        theWaveSize;
  103.     LQueue        theFadeQueue;
  104.     Int32        theDummyTicks;
  105.  
  106.     while ( ! mLightQueue.IsEmpty() ) {
  107.         
  108.         // All neurons which are currently in the queue belong to
  109.         // the same "wave". Here we check the size of the wave.
  110.         
  111.         theWaveSize = mLightQueue.GetSize();
  112.         
  113.         for ( int i = 1; i <= theWaveSize; i++ ) {
  114.             theNeuron = ( CNeuronPtr ) mLightQueue.NextGet();
  115.             theNeuron -> DoLightUpAction();
  116.             
  117.             theFadeQueue.NextPut( theNeuron );
  118.         }
  119.  
  120.         // Force redraw and stop for a while so that the user can
  121.         // enjoy the full effect of the "animation"... 
  122.         
  123.         RequestDraw();
  124.         ::Delay( 20, &theDummyTicks );
  125.         
  126.         while ( ! theFadeQueue.IsEmpty() ) {
  127.             theNeuron = ( CNeuronPtr ) theFadeQueue.NextGet();
  128.             theNeuron -> SetPostLightUpState();
  129.         }
  130.         
  131.         RequestDraw();
  132.     }
  133. }
  134.  
  135. // ---------------------------------------------------------------------------
  136. //        • InitMatrix
  137. //
  138. //          Called by:    CStdNeuralNet::CStdNeuralNet
  139. // ---------------------------------------------------------------------------
  140. //  Derived classes should call this function right after CreateMatrix()
  141.  
  142. void
  143. CNeuralNet::InitMatrix( const SGenParams & inParams )
  144. {
  145.     CNeuronPtr    theNeuron;
  146.  
  147.     CNeuron::SetNet( this );
  148.     
  149.     for ( int theRow = 1; theRow <= mSize; theRow++ ) {
  150.         for ( int theCol = 1; theCol <= mSize; theCol++ ) {
  151.             theNeuron = GetNeuron( theCol, theRow );
  152.             theNeuron -> SetNeuronPos( theCol, theRow );
  153.             
  154.             GenerateConnections( *theNeuron, inParams );
  155.         }
  156.     }
  157. }
  158.  
  159. // ---------------------------------------------------------------------------
  160. //        • ValidLocation
  161. //
  162. //          Called by:    CNeuralNet::GenerateConnections
  163. // ---------------------------------------------------------------------------
  164. //    Check whether a location (column+row) is inside the matrix bounds.
  165.  
  166. inline Boolean
  167. CNeuralNet::ValidLocation( Int16 inCol, Int16 inRow ) const
  168. {
  169.     return ( inCol > 0 && inCol <= mSize &&
  170.              inRow > 0 && inRow <= mSize );
  171. }
  172.  
  173. // ---------------------------------------------------------------------------
  174. //        • GenerateConnections
  175. //
  176. //          Called by:    CNeuralNet::InitMatrix
  177. // ---------------------------------------------------------------------------
  178. //    Generate random connections for the given neuron (inNeuron).  
  179. //    The generation is guided by the parameters (inParams) set by the user.
  180. //    We also set the "connection rect" (mConnRect) of the neuron. This
  181. //    rectangle is big enough to include all the neurons which inNeuron
  182. //    is connected to.
  183.  
  184. void
  185. CNeuralNet::GenerateConnections( 
  186.     CNeuron             & inNeuron,
  187.     const SGenParams & inParams ) const
  188. {
  189.     Int16    theQuantity,
  190.             theCol, theRow,            // Location of inNeuron
  191.             theAvgCol, theAvgRow,
  192.             theNewCol, theNewRow;
  193.     Rect    theRect;                // Rect which is used for 
  194.                                     // calculating the "connection rect"
  195.  
  196.     theQuantity = RangedRdm( inParams.qtyMin, inParams.qtyMax );
  197.     inNeuron.GetNeuronPos( theCol, theRow );
  198.     
  199.     SetRect (    &theRect,            // Left, top, right, bottom...
  200.                 theCol,    theRow, theCol, theRow );
  201.     
  202.     // Our [1,1] square is in the upper left corner of the matrix, but
  203.     // the user doesn't really know this, so we can set the positive
  204.     // directions to be RIGHT and UP. Therefore we ADD the average
  205.     // "X-length" (columns) and SUBSTRACT the average "Y-length" (rows).
  206.     
  207.     theAvgCol = theCol + inParams.xLengthAvg;
  208.     theAvgRow = theRow - inParams.yLengthAvg;
  209.  
  210.     // The location picked by random must...
  211.     //    • ...be a valid location inside the matrix
  212.     //    • ...not be the same where inNeuron is situated
  213.     //    • ...not be a location to which inNeuron already has a connection
  214.     //
  215.     // Note: If the location is rejected, the loop counter (i) is still
  216.     //          incresed by one. In other words, theQuantity is actually 
  217.     //         the maximum number of connections which is possible, but 
  218.     //         the neuron gets less connections if any of the randomly 
  219.     //         picked locations are rejected !!
  220.     
  221.     for ( int i = 1; i <= theQuantity; i++ ) {
  222.     
  223.         theNewCol = RangedRdm(    theAvgCol - inParams.xLengthDev,
  224.                                 theAvgCol + inParams.xLengthDev );
  225.         theNewRow = RangedRdm(    theAvgRow - inParams.yLengthDev,
  226.                                 theAvgRow + inParams.yLengthDev );
  227.         
  228.         if ( ValidLocation( theNewCol, theNewRow ) &&
  229.              ( theNewCol != theCol || theNewRow != theRow ) ) {
  230.              
  231.              CNeuronPtr    theNeuron = GetNeuron( theNewCol, theNewRow );
  232.              
  233.              if ( ! inNeuron.IsConnectedTo( theNeuron ) ) {
  234.              
  235.                  inNeuron.AddToNeuronList( theNeuron );
  236.                  
  237.                  // Update the (temporary) connection rect
  238.                  
  239.                  if ( theNewCol < theRect.left )
  240.                      theRect.left = theNewCol;
  241.                  if ( theNewCol > theRect.right )
  242.                      theRect.right = theNewCol;
  243.                  if ( theNewRow < theRect.top )
  244.                      theRect.top = theNewRow;
  245.                  if ( theNewRow > theRect.bottom )
  246.                      theRect.bottom = theNewRow;
  247.             }
  248.         }
  249.     }
  250.     
  251.     inNeuron.SetConnectionRect( theRect );
  252. }
  253.  
  254. // ---------------------------------------------------------------------------
  255. //        • GetRandomReceptor
  256. //
  257. //          Called by:    CNeuralNet::SpendTime
  258. // ---------------------------------------------------------------------------
  259. //    Return a pointer to a random receptor
  260.  
  261. inline CNeuronPtr
  262. CNeuralNet::GetRandomReceptor( ) const
  263. {
  264.     Int16 theRow = RangedRdm( 1, mSize );
  265.     return GetNeuron( 1, theRow );
  266. }
  267.  
  268. // ---------------------------------------------------------------------------
  269. //        • SpendTime
  270. //
  271. //          Called by:    LPeriodical::DevoteTimeToIdlers
  272. // ---------------------------------------------------------------------------
  273. //    This function overrides the pure virtual function in LPeriodical.
  274. //    SpendTime gets called after every single Idle Event (= often...).
  275. //    If there aren't any neurons in the "light up queue" and the demo mode
  276. //    is on, we pick a random receptor and simulate a click on it.
  277.  
  278. void
  279. CNeuralNet::SpendTime( const EventRecord & /*inMacEvent*/ )
  280. {
  281.     CNeuronPtr    theNeuron;
  282.     Int32        theDummyTicks;
  283.  
  284.     if (! mLightQueue.IsEmpty())
  285.         ProcessLightQ();
  286.  
  287.     else if ( DemoModeOn() ) {
  288.         ::Delay( 15, &theDummyTicks );        // Wait a moment (1/4 s)...
  289.         theNeuron = GetRandomReceptor();
  290.         theNeuron -> DoClickAction( false );
  291.     }
  292. }
  293.  
  294. // ---------------------------------------------------------------------------
  295. //        • SetNeuronsDirty
  296. //
  297. //          Called by:    CStdNeuron::DoClickAction
  298. //                        CStdNeuron::DoLightUpAction
  299. //                        CStdNeuron::SetPostLightUpState
  300. // ---------------------------------------------------------------------------
  301. //    Force every neuron inside the given rectangle to be redrawn.
  302.  
  303. void 
  304. CNeuralNet::SetNeuronsDirty( const Rect & inRect ) const
  305. {
  306.     for ( Int16 theRow = inRect.top; theRow <= inRect.bottom; theRow++ )
  307.         for ( Int16 theCol = inRect.left; theCol <= inRect.right; theCol++ ) {
  308.         
  309.             CNeuronPtr    theNeuron = GetNeuron( theCol, theRow );
  310.  
  311.                         theNeuron -> ForceRedraw();
  312.         }
  313. }
  314.  
  315. // ---------------------------------------------------------------------------
  316. //        • DrawNeurons
  317. //
  318. //          Called by:    CNeuroSimPane::DrawNeurons
  319. // ---------------------------------------------------------------------------
  320. //    Draw all the neurons which need redrawing. Any neuron's location 
  321. //    on the screen is calculated from the location of the neuron in the
  322. //    first column of the first row (inOneOneRect).
  323.  
  324. void 
  325. CNeuralNet::DrawNeurons(
  326.     const Rect &inOneOneRect,
  327.     Uint16        inSquareSize ) const
  328. {
  329.     Rect        theNeuronRect;
  330.     CNeuronPtr    theNeuron;
  331.  
  332.     for ( Int16 theRow = 1; theRow <= mSize; theRow++ )
  333.         for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
  334.  
  335.             theNeuron = GetNeuron( theCol, theRow );
  336.             
  337.             if ( theNeuron -> RedrawNeeded() ) {
  338.                 theNeuronRect = inOneOneRect;
  339.     
  340.                 ::OffsetRect(     &theNeuronRect,
  341.                                 (theCol - 1) * inSquareSize,
  342.                                 (theRow - 1) * inSquareSize );
  343.             
  344.                  theNeuron -> Draw( theNeuronRect );
  345.             }
  346.         }
  347. }
  348.  
  349. // ---------------------------------------------------------------------------
  350. //        • DrawConnections
  351. //
  352. //          Called by:    CNeuroSimPane::DrawConnections
  353. // ---------------------------------------------------------------------------
  354. //    Tell every neuron to redraw its connections - if necessary.
  355.  
  356. void 
  357. CNeuralNet::DrawConnections() const
  358. {
  359.     for ( Int16 theRow = 1; theRow <= mSize; theRow++ )
  360.         for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
  361.  
  362.             CNeuronPtr    theNeuron = GetNeuron( theCol, theRow );
  363.             
  364.             if ( theNeuron -> ConnectionsDirty() )
  365.                 theNeuron -> DrawConnections();
  366.         }
  367. }
  368.  
  369. // ---------------------------------------------------------------------------
  370. //        • SetCenterPoints
  371. //
  372. //          Called by:    CNeuroSimPane::SetNet
  373. // ---------------------------------------------------------------------------
  374. //  This function must be called before calling the DrawConnections
  375. //  function for the first time. Here we set the center points of the
  376. //  neurons. We do this so that we don't have to calculate the center
  377. //  points every time we draw the connections...
  378.  
  379. void 
  380. CNeuralNet::SetCenterPoints( Uint16 inOneOneXY, Uint16 inSquareSize) const
  381. {
  382.     Point    theCenter;
  383.  
  384.     for ( Int16 theRow = 1; theRow <= mSize; theRow++ ) {
  385.         for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
  386.  
  387.             CNeuronPtr    theNeuron = GetNeuron( theCol, theRow );
  388.             
  389.             theCenter.h = inOneOneXY + (theCol - 1) * inSquareSize;
  390.             theCenter.v = inOneOneXY + (theRow - 1) * inSquareSize;
  391.             
  392.             theNeuron -> SetCenterPoint( theCenter );
  393.         }
  394.     }
  395. }
  396.